package wh.iolists;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.JPanel;

import org.json.JSONException;
import wh.Customer;
import wh.Order;
import wh.ProcessedOrder;
import wh.exceptions.BadIDFormat;
import wh.exceptions.IDCollisionException;
import wh.exceptions.InvalidValueException;
import wh.myjson.JAStorageDisk;
import wh.myjson.JSONCustomer;
import wh.myjson.JSONStorageDisk;
import wh.products.SAS;
import wh.products.SSD;
import wh.products.StorageDisk;

public class StorageDiskList 
{
	// private static ArrayList<StorageDisk> productMap = new ArrayList<StorageDisk>();
	// Collections.synchronizedMap() guarantees that each atomic operation you want to run on the map will be synchronized.
		// But it must be synchronized in a block, externally, wich it is.
		// From documentation -> This Map is fail-fast: if the map is structurally modified at any time after the iterator is created, 
		// in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. 
		// Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, 
		// non-deterministic behavior at an undetermined time in the future.
		// This is suitable when considering a large product management enterprise (a response to requirements).
	private static Map<String, StorageDisk> productMap = Collections.synchronizedMap(new HashMap<String, StorageDisk>());
	private static final String SUBCLASS_SSD = "SSD";
	private static final String SUBCLASS_HDD = "HDD";
	private static StorageDiskList instance = null; // Singleton purpose
	
	////////Singleton part Begin //////////////
	
	protected StorageDiskList() {
		// Exists only to defeat instantiation.
	}

	public static StorageDiskList getInstance() {

		if (instance == null)
		{
			instance = new StorageDiskList();
		}
		return instance;
	}
	///////// Singleton part End ////////////////
	
	/**
	 * Loads StorageDisks (SSD or HDD) from StorageDisk.txt
	 */
	public void restoreProducts()
	{
		try {
            
            FileReader fileReader = new FileReader(new File("StorageDisk.txt"));
            BufferedReader buffR = new BufferedReader(fileReader);
            
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = buffR.readLine()) != null) {
                sb.append(line);
            }
            
            JAStorageDisk ja = new JAStorageDisk(sb.toString());
            
            for (int i = 0; i < ja.length(); i++) {
                
            	JSONStorageDisk jo = new JSONStorageDisk(ja.get(i).toString());
            	String subClass = jo.getString("subType").trim();
            	String productID = jo.getString("pID").trim();
            	
            	System.out.println("productID from file: "+productID);
            	
            	if (SUBCLASS_SSD.equals(subClass))
            		productMap.put(productID, createSSD(jo));
            	
            	else if (SUBCLASS_HDD.equals(subClass))
            		productMap.put(productID, createHDD(jo));
            		
            }
            
        } catch (JSONException ex) {
        	ex.printStackTrace();
        } catch (FileNotFoundException ex) {
        	ex.printStackTrace();
        } 
        catch (IOException ex) {
        	ex.printStackTrace();
        }
	}
	
	public StorageDisk getProduct(String key)
    {
    	return productMap.get(key);
    }
	
	public int getProductsListSize()
	{
		return productMap.size();
	}
	
	public Iterator<Entry<String, StorageDisk>> getIterator()
	{
		return productMap.entrySet().iterator();
	}
	
	/**
	 * Updates the stock count for product items (in-memory requirement).
	 * @param proOrder
	 */
	public void updateInStockState(ProcessedOrder proOrder)
    {
		if (productMap.containsKey(proOrder.getProductID())) {
			
			StorageDisk disk = productMap.get(proOrder.getProductID());
			disk.setNewInStock(proOrder.getQuantity());
		}
    }
	
	public double getProductPrice(String pID)
	{
		double price = 0;
	
		if (productMap.containsKey(pID))
			price = productMap.get(pID).getPrice();
		
		return price;
	}
	
	public synchronized Object[] getPriceAndInStock(String pID)
	{
		Object[] priceAndStock = new Object[2]; // price in index 0, and stock quantity in index 1. To avoid using two method calls and two iterations over productMap
		
		if (productMap.containsKey(pID)) {
			
			StorageDisk disk = productMap.get(pID);
			priceAndStock[0] = (Double) disk.getPrice();
			priceAndStock[1] = (Integer) disk.getInStock();
			
		}
		return priceAndStock;
	}
	
	
	public static boolean hasProductID(String idRef) 
	{
		return productMap.containsKey(idRef);
	}
	
	public static void CompareProductIDs(String id) throws IDCollisionException
	{
		if (productMap.containsKey(id)) 
			throw new IDCollisionException(id);	
	}
	
	private SAS createHDD(JSONStorageDisk jo)
	{
		SAS sd = null;
		try {
			sd = new SAS(jo.getString("pID"), jo.getDouble("price"), jo.getInt("inStock"), jo.getString("subType"), 
					jo.getString("manufacturer"), jo.getString("series"), jo.getString("productName"), jo.getInt("capacity"), 
					jo.getString("interface"), jo.getInt("mtbf"), jo.getInt("rotationSpeed"), jo.getInt("bufferSize"));
			
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (BadIDFormat e) {
			// Correct it
			e.printStackTrace();
		} catch (IDCollisionException e) {
			// Correct it
			e.printStackTrace();
		} catch (InvalidValueException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return sd;
	}
	
	private SSD createSSD(JSONStorageDisk jo)
	{
		SSD sd = null;
		try {
			sd = new SSD(jo.getString("pID"), jo.getDouble("price"), jo.getInt("inStock"), jo.getString("subType"), 
					jo.getString("manufacturer"), jo.getString("series"), jo.getString("productName"), jo.getInt("capacity"), 
					jo.getString("interface"), jo.getInt("mtbf"), jo.getInt("writeIOPS"), jo.getString("cellLevel"), jo.getBoolean("hasTRIM"));
			
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (BadIDFormat e) {
			// Correct it
			e.printStackTrace();
		} catch (IDCollisionException e) {
			// Correct it
			e.printStackTrace();
		} catch (InvalidValueException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return sd;
	}

}
